// 云函数入口文件
const cloud = require('wx-server-sdk')
var rp = require('request-promise')
const crypto = require('crypto');


//1. 配置参数
var smallDistance = 0.2 / 100; //小网格波动百分比%
var bigDistance = 0.3 / 100; //大网格波动百分比%

var rate = 0.2 / 100; //交易所交易费率百分比%

var SMALL_DISTANCE_NUM = 2; //小网格数量
var BIG_DISTANCE_NUM = 1; //大网格数量

// 交易对
var accountKey = "qc"; // 货币
var cointsKey = "eos"; // 币种

//数据库参数：集合account中创建的记录ID
var dbName = "bf55b7265f257df500016ef860358507";

// ZB交易所的系统参数
var SecretKey = "e4478b7a-7bfe-4f60-a8f9-3964e6681ea4";
var AccessKey = "ab34d6bb-25a0-4146-9935-2577a684cb0d";


var apiURL = "http://api.zb.live/data/v1";
var tradeURL = "https://trade.zb.live/api";
 
var AllTimes = SMALL_DISTANCE_NUM * BIG_DISTANCE_NUM;

var startPrice = 0; //基价
var buyPriceList = []; //买入的价格和数量
var tradeID; //当前委托单的ID

var startBalance = 0; //初始资金
var startCointNums = 0; //一直持币的价值
var AllRate = 0; //所有手续费
var AllProfit = 0; //所有利润
 

//市场参数默认值,通过接口获取更新
var priceScale = 2;
var amountScale = 4;
var Balance = 0; //账户余额
var Coins = 0; //持币数量
var ticker; //当前市场价格



//云数据库初始化
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()

exports.main = async (event, context) => {
  //本函数由定时器触发   

  console.log("----------------------检测启动-----------------------------")
  //获取交易所配置
  await getMarkets();

  console.log("|* 读取数据库参数")
  //云数据库操作
  await db.collection('account').doc(dbName).get().then(res => {
    //获取数据库参数
    startPrice = res.data.startPrice; //基价
    startCointNums = res.data.startCointNums; //一直持币的价值
    startBalance = res.data.startBalance; //初始资金
    buyPriceList = res.data.buyPriceList; //买入的价格和数量
    AllRate = res.data.AllRate; //所有手续费
    AllProfit = res.data.AllProfit; //所有利润
    tradeID = res.data.tradeID;
    tradeIDbuy = res.data.tradeIDbuy; //买单订单id
    console.log("| -【网格基价】" + startPrice)
    console.log("| -【初始资金可购币】", startCointNums)
    console.log("| -【网格布网列表】", buyPriceList)
    console.log("| -【所有手续费】", AllRate)
    console.log("| -【所有利润】", AllProfit)

  })

  if (buyPriceList == null) {
    buyPriceList = []
  }

  if (startCointNums == null) {
    startCointNums = 0
  }

  if (startPrice == null) {
    startPrice = 0
  }
  if (AllRate == null) {
    AllRate = 0
  }

  if (AllProfit == null) {
    AllProfit = 0
  }


  if ((tradeID != null) || (tradeIDbuy != null)) {
    console.log("---------------网格交易模式---------------------")

    //获取委托单信息
    var tradeInfo;
    var tradeInfoBuy;

    if (tradeID != null && tradeID != "") {
      //卖单
      console.log("| -【委托中卖单订单ID】" + tradeID)

      await getOrder(tradeID).then(function (resolve) {
        tradeInfo = resolve
      }, function (reject) {});


      if ((tradeInfo.status == 1 || tradeInfo.status == 2)) {
        //已成交或者成交部分,计算实际成交的情况
        var tradePrice; //成交价格
        //卖单
        var thisTimeRate = adjustFloat(tradeInfo.trade_money * rate, priceScale);
        AllRate = AllRate + thisTimeRate;

        //未成交的数量
        var untradeAmout = tradeInfo.total_amount - tradeInfo.trade_amount;
        var buyPrice = buyPriceList[buyPriceList.length - 1].buyPrice;
        //计算利润
        AllProfit = AllProfit + adjustFloat((tradeInfo.trade_money - (buyPrice * tradeInfo.trade_amount) - thisTimeRate), priceScale)

        tradePrice = adjustFloat((tradeInfo.trade_money / tradeInfo.trade_amount), priceScale)

        //判断未成交的是否低于交易最小量，如是则认为完成交易，如不是，则留着下个轮回再卖
        if (untradeAmout < Math.pow(0.1, amountScale)) {
          //完成交易
          buyPriceList.pop()
          //取消买单
          await cancelOrder(tradeIDbuy)

        } else {
          //只完成部分
          buyPriceList.pop()
          buyPriceList.push({
            "buyPrice": buyPrice,
            "num": untradeAmout
          })

          //取消剩余订单
          await cancelOrder(tradeID)
          await cancelOrder(tradeIDbuy)
        }


        if (buyPriceList.length == 0) {
          //当全部收网后，startPrice设置为最后的卖出价
          startPrice = buyPrice
          console.log("| 【已清网,更新基价】" + startPrice)
        }

        sendMes("卖出", buyPrice, tradeInfo.trade_amount, AllProfit, AllRate);

        if (buyPriceList.length != 0) {
          console.log("|【当前撒网列表】" + JSON.stringify(buyPriceList))
        }

        console.log("|【手续费】" + AllRate, "【总利润】" + AllProfit)

        //本轮结束，将数据保存到数据库
        await db.collection('account').doc(dbName).update({
          data: {
            buyPriceList: buyPriceList, //买入的价格和数量
            AllRate: AllRate, //所有手续费
            AllProfit: AllProfit, //所有利润
            tradeID: null,
            tradeIDbuy: null,
          },
          success: function (res) {
            console.log(res.data)
          }
        })

        if (tradeInfo.trade_amount != 0) {
          //记录交易记录到数据库
          db.collection('trade').add({
            data: {
              type: "卖出",
              price: tradePrice,
              num: tradeInfo.trade_amount,
              time: new Date()
            },
          })
        }
      }

    }

    if (tradeIDbuy != null && tradeIDbuy != "") {
      console.log("| -【委托中买单订单ID】" + tradeIDbuy)

      await getOrder(tradeIDbuy).then(function (resolve) {
        tradeInfoBuy = resolve
      }, function (reject) {});


      if ((tradeInfoBuy.status == 1 || tradeInfoBuy.status == 2)) {
        //取消剩余订单
        await cancelOrder(tradeID)
        await cancelOrder(tradeIDbuy)

        var tradePrice; //成交价格
        //买单
        if (tradeInfoBuy.trade_amount > Math.pow(0.1, amountScale)) {
          tradePrice = adjustFloat(tradeInfoBuy.trade_money / tradeInfoBuy.trade_amount, priceScale);
          buyPriceList.push({
            "buyPrice": tradePrice,
            "num": tradeInfoBuy.trade_amount
          })

          var thisTimeRate = adjustFloat(tradeInfoBuy.trade_money * rate, priceScale);
          AllRate = AllRate + thisTimeRate;
          //利润要先减去本次的手续费
          AllProfit = AllProfit - thisTimeRate;

          sendMes("买入", tradePrice, tradeInfoBuy.trade_amount, AllProfit, AllRate);

        } else {
          console.log("｜【交易未完成】")
        }

        if (buyPriceList.length != 0) {
          console.log("|【当前撒网列表】" + JSON.stringify(buyPriceList))
        }

        console.log("|【手续费】" + AllRate, "【总利润】" + AllProfit)

        //本轮结束，将数据保存到数据库
        await db.collection('account').doc(dbName).update({
          data: {
            buyPriceList: buyPriceList, //买入的价格和数量
            AllRate: AllRate, //所有手续费
            AllProfit: AllProfit, //所有利润
            tradeID: null,
            tradeIDbuy: null,
          },
          success: function (res) {
            console.log(res.data)
          }
        })

        if (tradeInfoBuy.trade_amount != 0) {
          //记录交易记录到数据库
          db.collection('trade').add({
            data: {
              type: "买入",
              price: tradePrice,
              num: tradeInfoBuy.trade_amount,
              time: new Date()
            },
          })
        }
      }

    }


    //获取价格信息
    await getTicker()

    var sellPrice = ticker.sell; //卖价
    var buyPrice = ticker.buy; //买价

    console.log("保存市场价格" + sellPrice)


    await db.collection('account').doc(dbName).update({
      data: {
        sellPrice: sellPrice,
        buyPrice: buyPrice,
        time: new Date()
      },
      success: function (res) {
        console.log(res.data)
      }
    })


  } else {
    console.log("----------判断模式---------------------")

    //判断模式
    console.log("|* 系统参数")
    console.log("| -【手续费】", rate * 100 + "%")
    console.log("| -【小网格】", smallDistance * 100 + "%")
    console.log("| -【大网格】", bigDistance * 100 + "%")

    //获取账号信息
    await getAccountInfo();
    console.log("-------------------------------")

    //获取价格信息
    await getTicker()


    var sellPrice = ticker.sell; //卖价
    var buyPrice = ticker.buy; //买价

    //基价初始化=当前市价
    if (startPrice == 0 || startPrice == null) {
      console.log("【状态】首次启动")
      startPrice = sellPrice;
      startCointNums = Balance / sellPrice; //一开始持币的数量
      startBalance = Balance;
      console.log("【设置基准价格】" + startPrice)
    }

    //确定买入价格和卖出价格
    var targetBuyPrice
    if (buyPriceList.length == 0) {
      //还没买入,找目标买入价格
      targetBuyPrice = (startPrice * (1 - smallDistance - rate)).toFixed(priceScale)
      console.log("| -【目标买入价格】" + targetBuyPrice)
      console.log("| -【基价】" + startPrice)
      //如果目标买入价格和当前市价偏移太远，则更新基价
      if (targetBuyPrice * (1 + bigDistance + rate) < sellPrice) {
        startPrice = sellPrice;
        console.log("| -【修正基价】" + startPrice)
      }
    } else {
      //已经撒网，确定下一个买入价格和卖出价格
      //确定卖出价格，买入和卖出都有手续费成本，需都加上
      var targetSellPrice = (buyPriceList[buyPriceList.length - 1].buyPrice * (1 + smallDistance + rate + rate)).toFixed(priceScale) //目标卖价
      if (buyPriceList.length % SMALL_DISTANCE_NUM == 0) {
        //大网格区间
        targetBuyPrice = (buyPriceList[buyPriceList.length - 1].buyPrice * (1 - bigDistance - rate)).toFixed(priceScale); //目标买价
        console.log("【当前处于大网格】")
      } else {
        //小网格区间
        targetBuyPrice = (buyPriceList[buyPriceList.length - 1].buyPrice * (1 - smallDistance - rate)).toFixed(priceScale); //目标买价
      }

      console.log("| -【目标买入价格】" + targetBuyPrice)
      console.log("| -【目标卖出价格】" + targetSellPrice)

    }


    //确定购买数量
    var buyNums = 0;
    if (AllTimes != buyPriceList.length) {
      buyNums = adjustFloat((Balance / (AllTimes - buyPriceList.length)) / (sellPrice * (1 + rate)), amountScale);
      console.log("| -【本次可以买入的数量】" + buyNums)
    }


    //挂单模式
    if (buyPriceList.length != 0) {
      console.log("| -【设定目标卖出价格，计划以" + targetSellPrice + "价卖出" + adjustFloat(buyPriceList[buyPriceList.length - 1].num * (1 - rate), amountScale), +"个】")
      //卖出
      var id = await orderTest(targetSellPrice, adjustFloat(buyPriceList[buyPriceList.length - 1].num * (1 - rate), amountScale), 0);
      console.log("|【发送卖出指令】交易ID:" + id)
      //将交易id保存到数据库
      await db.collection('account').doc(dbName).update({
        data: { 
          tradeID: id, 
        },
        success: function (res) {
          console.log(res.data)
        }
      })
    }

    if (buyNums > (Math.pow(0.1, amountScale)) && buyPriceList.length != AllTimes) {
      console.log("| 【设定目标买入价格，计划以" + targetBuyPrice + "价买入" + buyNums + "个】")
      //买入
      var idBuy = await orderTest(targetBuyPrice, buyNums, 1);
      console.log("|【发送买入指令】交易ID:" + idBuy)
    }


    if (buyPriceList.length != 0) {
      console.log("|【当前撒网列表】" + JSON.stringify(buyPriceList))
    }
    console.log("|【如果一直持币的价值】" + startCointNums * buyPrice, "【当前价值】" + (Balance + Coins * buyPrice))
    console.log("|【手续费】" + AllRate, "【总利润】" + AllProfit)


    //本轮结束，将数据保存到数据库
    await db.collection('account').doc(dbName).update({
      data: {
        startPrice: startPrice, //基价
        startCointNums: startCointNums,
        startBalance: startBalance,
        Balance: Balance,
        Coins: Coins,
        tradeIDbuy: idBuy,
        targetBuyPrice: targetBuyPrice,
        targetSellPrice: targetSellPrice,
        sellPrice: sellPrice,
        buyPrice: buyPrice,
        time: new Date()

      },
      success: function (res) {
        console.log(res.data)
      }
    })


  }



  console.log("----------------------检测结束-----------------------------")


}


//保留小数点后三位
function adjustFloat(num, length) {
  return Math.floor(num * Math.pow(10, length)) / Math.pow(10, length);
}


/**
 * 获取市场配置
 */
function getMarkets() {
  return getRequest(apiURL + "/markets").then(function (res) {
    priceScale = res[cointsKey + "_" + accountKey].priceScale;
    amountScale = res[cointsKey + "_" + accountKey].amountScale;
    console.log("| * 市场配置")
    console.log("| -【数量小数点】" + amountScale)
    console.log("| -【价格小数点】" + priceScale)

  }, function (err) {
    console.log("getMarkets error:" + err)
  })

}



/**
 * 获取ticker数据
 */
function getTicker() {
  console.log("| * 实时市场价格")
  return getRequest(apiURL + "/ticker?market=" + cointsKey + "_" + accountKey).then(function (res) {
    console.log("| -【卖价】" + JSON.stringify(res.ticker.sell))
    console.log("| -【买价】" + JSON.stringify(res.ticker.buy))
    ticker = res.ticker
  }, function (err) {
    console.log(err)
  })


}


/**
 * 下单
 * tradeType: 交易类型1 / 0[buy / sell]
 */
function order(price, amount, tradeType) {
  var parameterObj = {
    accesskey: AccessKey,
    amount: amount,
    price: price,
    tradeType: tradeType,
    method: "order",
    currency: cointsKey + "_" + accountKey,
    acctType: 0, //杠杆 1/0[杠杆/现货]（选填,默认为: 0 现货） 
  }

  return requestSign(tradeURL + "/order", SecretKey, parameterObj).then(function (resolove) {
    return resolove.id
  }, function (reject) {
    console.log("order error" + JSON.string(reject))
    return reject
  })

}




/**
 * 下单
 */
async function orderTest(price, amount, tradeType) {
  console.log("交易所下单：" + price + "元 *" + amount + "个")
  //实际
  var id = await order(price, amount, tradeType)
  return id;
}








/**
 * 获取委托单信息
 * 
 */
function getOrder(id) {
  var parameterObj = {
    method: "getOrder",
    accesskey: AccessKey,
    currency: cointsKey + "_" + accountKey,
    id: id
  }

  return requestSign(tradeURL + "/getOrder", SecretKey, parameterObj).then(function (resolove) {
    console.log(JSON.stringify(resolove))
    return resolove
  }, function (reject) {
    console.log("cancelOrder error" + JSON.string(reject))
    return reject
  })

}


/**
 * 取消委托
 * 
 */
function cancelOrder(id) {
  var parameterObj = {
    method: "cancelOrder",
    accesskey: AccessKey,
    currency: cointsKey + "_" + accountKey,
    id: id
  }

  return requestSign(tradeURL + "/cancelOrder", SecretKey, parameterObj).then(function (resolove) {
    return resolove
  }, function (reject) {
    console.log("cancelOrder error" + JSON.string(reject))
    return reject
  })

}




/**
 * 未成交或部份成交挂单
 * 获取未成交或部份成交的买单和卖单，每次请求返回pageSize<=10条记录
 * 
 */
async function getUnfinishedOrdersIgnoreTradeType(pageIndex) {
  var parameterObj = {
    method: "getUnfinishedOrdersIgnoreTradeType",
    accesskey: AccessKey,
    currency: cointsKey + "_" + accountKey,
    pageIndex: pageIndex,
    pageSize: 10
  }

  await requestSign(tradeURL + "/getUnfinishedOrdersIgnoreTradeType", SecretKey, parameterObj).then(function (resolove) {
    return resolove
  }, function (reject) {
    console.log("cancelOrder error" + JSON.string(reject))
    return reject
  })

}


/**
 * 获取账号信息
 */
function getAccountInfo() {
  var parameterObj = {
    method: "getAccountInfo",
    accesskey: AccessKey,
  }

  console.log("| * 账号信息")
  return requestSign(tradeURL + "/getAccountInfo", SecretKey, parameterObj).then(function (resolove) {
    //将当前交易对的账号信息选出来
    for (var i = 0; i < resolove.result.coins.length; i++) {
      if (resolove.result.coins[i].key == cointsKey) {
        console.log("| -【持币】" + resolove.result.coins[i].available)
        console.log("| -【冻结币】" + resolove.result.coins[i].freez)
        Coins = resolove.result.coins[i].available;
      }

      if (resolove.result.coins[i].key == accountKey) {
        Balance = Number(resolove.result.coins[i].available);
        console.log("| -【资金】" + Balance)
        console.log("| -【冻结资金】" + resolove.result.coins[i].freez)
      }
    }
    return (resolove)
  }, function (reject) {
    console.log(reject)
    return (reject)
  })

}



/**
 * 带签名的请求方法
 */
async function requestSign(url, secretKey, parameterObj) {
  var ASCIIparameter = obj2str(parameterObj) //按ASCII排序参数 
  var sign = getSign(secretKey, ASCIIparameter); //请求加密签名串
  var reqTime = new Date().getTime();

  return new Promise(function (resolve, reject) {
    getRequest(url + "?" + ASCIIparameter + "&sign=" + sign + "&reqTime=" + reqTime).then(function (res) {
      //console.log("res:" + JSON.stringify(res))
      resolve(res)
    }, function (err) {
      reject(err)
    })
  })
}


/**
 * 生成签名
 * parameter:请求参数签名串
 */
function getSign(secretKey, parameter) {
  const sha1 = crypto.createHash('sha1');
  var sha14SKey = sha1.update(secretKey).digest('hex') //sha1加密
  const hmac = crypto.createHmac('md5', sha14SKey);
  var sign = hmac.update(parameter).digest('hex')
  return sign;
}



//object转string,用于签名计算
function obj2str(args) {
  var keys = Object.keys(args)
  keys = keys.sort() //参数名ASCII码从小到大排序（字典序）；
  var newArgs = {}
  keys.forEach(function (key) {
    if (args[key] != "" && args[key] != 'undefined') { //如果参数的值为空不参与签名；
      newArgs[key] = args[key] //参数名区分大小写；
    }

  })
  var string = ''
  for (var k in newArgs) {
    string += '&' + k + '=' + newArgs[k]
  }
  string = string.substr(1)
  return string
}


/**
 * Get 请求
 */
function getRequest(url) {
  var options = {
    uri: url,
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36'
    },
    json: true // Automatically parses the JSON string in the response
  };

  return new Promise(function (resolve, reject) {
    rp(options)
      .then(function (repos) {
        resolve(repos)
        // console.log('repos', repos);
      })
      .catch(function (err) {
        console.log("error:" + err)
        reject(err)
      });
  })
}



/**
 * 发送消息
 * touser： 接收者ID，替换为自己的微信OpenID
 * templateId: 模版ID
 */
function sendMes(type, price, num, AllProfit, AllRate) {
  try {
    const result = cloud.openapi.subscribeMessage.send({
      touser: 'ofJv15XIHKTsCMJNJaXliMpO7XjQ',
      page: 'pages/index/index',
      lang: 'zh_CN',
      data: {
        phrase1: {
          value: type
        },
        thing2: {
          value: "价格" + price + "数量" + num
        },
        character_string3: {
          value: '202003031212'
        },
        thing4: {
          value: "利润" + AllProfit + "手续费" + AllRate
        }
      },
      templateId: 'zFr4NhqEZAOfHKq4Soy7iMJKj9GbrRiH95NvAe_oO6U',
      miniprogramState: 'developer'
    })
    console.log(result)
    return result
  } catch (err) {
    console.log(err)
    return err
  }
}